-- SummonDisplay
-- Created by chengb Apr/28/2015
-- 召唤兽外观显示

require "game/ui/module/summon/SummonAnimation"

FRAME_STATE_INIT    = 1;
FRAME_STATE_PLAYING = 2;
FRAME_STATE_FINISH  = 3;

-- SummonDisplay继承自Node
SummonDisplay = class("SummonDisplay", function()
    return cc.Node:create();
end);

function SummonDisplay.create(classId)
    return SummonDisplay.new(classId);
end

-- 构造函数
function SummonDisplay:ctor(classId)
    -- 记录召唤兽编号
    self.classId = classId;

    -- 创建动画
    local animName = SummonM.query(classId, "animation");
    local animation = SummonAnimation.create(animName .. ".csb");
    animation:setName("animation");
    self.animation = animation;
    self:addChild(animation);

    -- 检查动作支持情况
    self:checkAction()

    -- 默认播放待机动作
    self:gotoIdle();

    self:registerScriptHandler(function(ev)
        if ev == "exitTransitionStart" then
            if self.animation then
                if self.animation.action then
                    self.animation.action:clearFrameEventCallFunc();
                end

                self.animation:destroy();
            end
        end
    end);
end

-- 切换怪物动作
function SummonDisplay:switchAction(actionType)
    if not self.actionsSupported[actionType] then
        -- 如果动作不支持，则保持现有动作
        return;
    end

    -- 更新动作类型
    self.actionType = actionType;

    -- 待机
    if SUMMON_ACTION_IDLE == actionType then
        self:gotoIdle();
    -- 受创
    elseif SUMMON_ACTION_DAMAGED == actionType then
        self:gotoDamaged();
    -- 攻击
    elseif SUMMON_ACTION_ATTACK == actionType then
        self:gotoAttack();
    -- 主动技
    elseif SUMMON_ACTION_CAST_SKILL == actionType then
        self:gotoCastSkill();
    else
        -- 暂不支持
        cclog("### 未知的召唤兽动作类型：" .. tostring(actionType));
    end
end

-- 播放待机动作
function SummonDisplay:gotoIdle()
    if self.animation == nil then
        return;
    end

    -- 获取帧范围和播放速度
    local frameRange = SummonM.query(self.classId, "idle_frame");
    local speed = SummonM.query(self.classId, "idle_speed");

    -- 更新动作类型
    local actionType = SUMMON_ACTION_IDLE;
    if self.actionType ~= actionType then
        self.actionType = actionType;
    end

    -- 播放
    self.animation:play(actionType, true, frameRange[1], frameRange[2], speed);
end

-- 震动
function SummonDisplay:shake()
    local offset = 20;
    local angle = 30;

    local moveRight = cc.MoveBy:create(0.05, cc.p(offset, 10));
    local rotateTo = cc.RotateTo:create(0.05, angle, angle);
    local delay = cc.DelayTime:create(0.1);
    local moveBack = cc.MoveBy:create(0.05, cc.p(-offset, -10));
    local rotateBack = cc.RotateTo:create(0.05, 0, 0);
    local callFunc = cc.CallFunc:create(function()
        self:setHighlightMode(self.animation, false);
    end);
    local action = cc.Sequence:create(cc.Spawn:create(moveRight, rotateTo), delay, cc.Spawn:create(moveBack, rotateBack), callFunc);

    self:runAction(action);
end

-- 播放受创动作
function SummonDisplay:gotoDamaged()
    if self.animation == nil then
        return;
    end

    -- 获取帧范围和播放速度
    local frameRange = SummonM.query(self.classId, "damaged_frame");
    local speed = SummonM.query(self.classId, "damaged_speed");

    if frameRange[1] <= 0 or frameRange[2] <= 0 or frameRange[1] > frameRange[2] then
    	trace("SummonDisplay", "召唤兽(%d)没有受创动作。", self.classId);

    	self:gotoIdle();
    	return;
    end

    -- 是否主动攻击
    local isActive = SummonM.query(self.classId, "active");

    if isActive == 1 then
        -- 主动攻击的召唤物，不播放受创动作
        -- 注册帧回调函数
        self:registerFrameEvent();
        return;
    end

    -- 更新动作类型
    local actionType = SUMMON_ACTION_DAMAGED;
    if self.actionType ~= actionType then
        self.actionType = actionType;
    end

    if speed == 0 then
        -- 没有受创动作， 改为震动效果
        self:shake();

        -- 注册帧回调函数
        self:registerFrameEvent();
        return;
    end

    -- 播放
    self.animation:play(actionType, false, frameRange[1], frameRange[2], speed);

    -- 注册帧回调函数
    self:registerFrameEvent();
end

-- 播放攻击动作
function SummonDisplay:gotoAttack()
    if self.animation == nil then
        return;
    end

    -- 获取帧范围和播放速度
    local frameRange = SummonM.query(self.classId, "attack_frame");
    local speed = SummonM.query(self.classId, "attack_speed");

    if frameRange[1] <= 0 or frameRange[2] <= 0 or frameRange[1] > frameRange[2] then
        trace("SummonDisplay", "召唤兽(%d)没有攻击动作。", self.classId);
        return;
    end

    -- 更新动作类型
    local actionType = SUMMON_ACTION_ATTACK;
    if self.actionType ~= actionType then
        self.actionType = actionType;
    end

    -- 播放
    self.animation:play(actionType, false, frameRange[1], frameRange[2], speed);

    -- 注册帧回调函数
    self:registerFrameEvent();
end

-- 播放主动技动作
function SummonDisplay:gotoCastSkill()
    if self.animation == nil then
        return;
    end

    -- 获取帧范围和播放速度
    local frameRange = SummonM.query(self.classId, "cast_frame");
    local speed = SummonM.query(self.classId, "cast_speed");

    if frameRange[1] <= 0 or frameRange[2] <= 0 or frameRange[1] > frameRange[2] then
        trace("SummonDisplay", "召唤兽(%d)没有攻击动作。", self.classId);
        return;
    end

    if speed == 0 then
        trace("SummonDisplay", "召唤兽(%d)主动技速度为0。", self.classId);
        return;
    end

    -- 更新动作类型
    local actionType = SUMMON_ACTION_CAST_SKILL;
    if self.actionType ~= actionType then
        self.actionType = actionType;
    end

    -- 播放
    self.animation:play(actionType, false, frameRange[1], frameRange[2], speed);

    -- 注册帧回调函数
    self:registerFrameEvent();
end

-----------------------------
-- 播放
-- @param startIndex   起始帧
-- @param endIndex     结束帧
-- @param loop         是否循环播放
-- @param speed        播放速度
-- @param callback     回调函数
function SummonDisplay:play(startIndex, endIndex, loop, speed, callback)
    if self.animation == nil then
        return;
    end

    if startIndex < 0 and endIndex < 0 then
        callback();
        return;
    end

    if startIndex > endIndex then
        callback();
        return;
    end

    self.animation.action:clearFrameEventCallFunc();

    -- 播放
    self.animation.action:setTimeSpeed(speed);
    self.animation.action:gotoFrameAndPlay(startIndex, endIndex, loop);
    self.curRange = {startIndex, endIndex};

    self:registerFrameEvent(callback);
end

-- 注册帧回调函数
function SummonDisplay:registerFrameEvent(callback)
    self.frameState = FRAME_STATE_INIT;

    -- 移除帧回调处理函数
    self.animation.action:clearFrameEventCallFunc();

    -- 帧回调处理函数
    local function onFrameEvent(frame)
        if nil == frame then
            return;
        end

        -- 使用pilor帧，确保每帧都会有回调过来
        if frame.getEvent and frame:getEvent() == "pilor" then
            -- 帧索引的下标是从0开始的，而配置的下标是从1开始的，故这里要加1
            local frameIndex = frame:getFrameIndex() + 1;

            if self.frameState == FRAME_STATE_INIT then
                if not self:isCurrentActionOver(frameIndex) then
                    self.frameState = FRAME_STATE_PLAYING;
                else
                    return;
                end
            end

            if not self.isCurrentActionOver or not self.animation then
                return;
            end

            -- 当前帧没有播放完毕
            if not self:isCurrentActionOver(frameIndex) then
                return;
            end

            -- 移除帧回调处理函数
            self.animation.action:clearFrameEventCallFunc();

            local function onFinish()
                self.frameState = FRAME_STATE_FINISH;
                if callback then
                    callback();
                else
                    -- 切换至待机状态
                    self:gotoIdle();
                end
            end

            performWithDelay(self, onFinish, 0.1);
        end
    end

    -- 设置帧回调处理函数
    self.animation.action:setFrameEventCallFunc(onFrameEvent);
end

-- 移除普通动画
function SummonDisplay:removeNormalAnimation()
    if self.animation == nil then
        return;
    end

    self.animation:pause();
    self.animation:destroy();
    self.animation = nil;
end

-- 检查当前动作是否支持
function SummonDisplay:checkAction()
    self.actionsSupported = {};
    for act, _ in pairs(ALL_SUMMON_ACTIONS) do
        local range = self:getFrameRange(act);
        if range == nil or (range[1] == 0 and range[2] == 0) then
            self.actionsSupported[act] = false;
        else
            self.actionsSupported[act] = true;
        end
    end
end

---------------------------
-- 获取帧范围
-- @param actionType 动作类型
function SummonDisplay:getFrameRange(actionType)
    local range;
    if actionType == SUMMON_ACTION_IDLE then
        range = SummonM.query(self.classId, "idle_frame");
    elseif actionType == SUMMON_ACTION_DAMAGED then
        range = SummonM.query(self.classId, "damaged_frame");
    elseif actionType == SUMMON_ACTION_ATTACK then
        range = SummonM.query(self.classId, "attack_frame");
    elseif actionType == SUMMON_ACTION_CAST_SKILL then
        range = SummonM.query(self.classId, "cast_frame");
    else
        trace("SummonDisplay", "无法获取帧范围，未知的动作类型：" .. tostring(actionType));
        return nil;
    end

    -- 检查参数合法性
    if self.animation then
        local maxIndex = self.animation.action:getDuration();
        if range[2] > maxIndex then
            range[2] = maxIndex;
        end
    end

    return range;
end

----------------------------
-- 判断当前动作是否已经播完
-- @param curFrameIndex   播放到第几帧
function SummonDisplay:isCurrentActionOver(curFrameIndex)
    local range;
    if not self.curRange then
        local curActionType = self.actionType;
        range = self:getFrameRange(curActionType);
        if not range then
            return false;
        end
    else
        range = self.curRange;
    end

    -- TODO 兼容最后一帧
    if curFrameIndex >= range[1] and curFrameIndex < range[2] - 1 then
        -- 还没播完
        return false;
    end

    return true;
end



